package myhashtable;

public class LinkedList {

    private int length;
    private Node node;
    private Node head;
    private Node tail;

    public LinkedList() {
        this.node = null;
        this.head = null;
        this.length = 0;
    }

    public void add(String key, Object obj) {
        if (head == null) {
            head = new Node(key, obj);
            tail = head;
            length++;
        } else {
            Node newNode = new Node(key, obj);
            tail.setNext(newNode);
            tail = newNode;
            length++;
        }
    }

    public void addWithSort(String key, Object obj) {
        int idx = 0;
        boolean isAlreadyAdd = false;
        Node tmpNode = head;
        Node tmpBefore = null;
        Node newNode = new Node(key, obj);
        do {
            if (key.compareTo(tmpNode.getKey()) < 0) {  //lebih dahulu
                if (idx == 0) {
                    newNode.setNext(tmpNode);
                    head = newNode;
                } else {
                    newNode.setNext(tmpNode);
                    tmpBefore.setNext(newNode);
                }
                isAlreadyAdd = true;
                break;
            } else if (key.compareTo(tmpNode.getKey()) == 0) {   //sama
                if (length == 1) {
                    head = newNode;
                    tail = newNode;
                } else if (idx == 0) {
                    newNode.setNext(tmpNode.getNext());
                    head = newNode;
                } else if (tmpNode.getNext() == null) {
                    tmpBefore.setNext(newNode);
                    tail = newNode;
                } else {
                    newNode.setNext(tmpNode.getNext());
                    tmpBefore.setNext(newNode);
                }
                isAlreadyAdd = true;
                break;
            }
            tmpBefore = tmpNode;
            tmpNode = tmpNode.getNext();
            idx++;
        } while (tmpNode != null);

        if (!isAlreadyAdd) {
            tail = newNode;
            tmpBefore.setNext(newNode);

        }
        length++;
    }

    public Object get(int i) {
        if (lengthInvalid(i)) {
            return null;
        }

        int idx = 0;
        Node tmpNode = head;
        while (idx < i) {
            tmpNode = tmpNode.getNext();
            idx++;
        }
        return tmpNode.getObj();
    }

    public Node getNode(int i) {
        if (lengthInvalid(i)) {
            return null;
        }

        int idx = 0;
        Node tmpNode = head;
        while (idx < i) {
            tmpNode = tmpNode.getNext();
            idx++;
        }
        return tmpNode;
    }

    /**
     * @param key
     * @return
     */
    public Object getByKey(String key) {
        if(length==0){
            return null;
        }
        Node tmpNode = head;
        do {
            if (key.compareTo(tmpNode.getKey()) == 0) {
                return tmpNode.getObj();
            }
            tmpNode = tmpNode.getNext();
        } while (tmpNode != null);
        return null;
    }

    public void set(int i, String key, Object obj) {
        if (lengthInvalid(i)) {
            return;
        }

        int idx = 0;
        Node tmpNode = head;
        Node tmpBefore = null;
        while (idx < i) {
            tmpBefore = tmpNode;
            tmpNode = tmpNode.getNext();
            idx++;
        }
        Node newNode = new Node(key, obj);
        if (length == 1) {
            head = newNode;
            tail = newNode;
        } else if (idx == 0) {
            newNode.setNext(tmpNode.getNext());
            head = newNode;
        } else if (tmpNode.getNext() == null) {
            tmpBefore.setNext(newNode);
            tail = newNode;
        } else {
            newNode.setNext(tmpNode.getNext());
            tmpBefore.setNext(newNode);
        }
    }

    public void remove(int i) {
        if (lengthInvalid(i)) {
            return;
        }

        int idx = 0;
        Node tmpNode = head;
        Node tmpBefore = null;
        while (idx < i) {
            tmpBefore = tmpNode;
            tmpNode = tmpNode.getNext();
            idx++;
        }
        if (length == 1) {
            head = null;
            tail = null;
        } else if (idx == 0) {
            head = tmpNode.getNext();
        } else if (tmpNode.getNext() == null) {
            tmpBefore.setNext(null);
            tail = tmpBefore;
        } else {
            tmpBefore.setNext(tmpNode.getNext());
        }
        length--;
    }

    public boolean removeByKey(String key) {
        if (length == 0) {
            return false;
        }

        Node tmpNode = head;
        Node tmpBefore = null;
        int idx = 0;
        do {
            if (key.compareTo(tmpNode.getKey()) == 0) {
                if (length == 1) {
                    head = null;
                    tail = null;
                } else if (idx == 0) {
                    head = tmpNode.getNext();
                } else if (tmpNode.getNext() == null) {
                    tmpBefore.setNext(null);
                    tail = tmpBefore;
                } else {
                    tmpBefore.setNext(tmpNode.getNext());
                }
                length--;
                return true;
            }
            tmpBefore = tmpNode;
            tmpNode = tmpNode.getNext();
            idx++;
        } while (tmpNode != null);
        return false;
    }

    public void show() {
        System.out.println("length:" + length);
        int idx = 0;
        Node tmpNode = head;
        while (idx < length) {
            System.out.println(idx + ":" + tmpNode.getKey() + "->" + tmpNode.getObj());
            tmpNode = tmpNode.getNext();
            idx++;
        }
    }

    private boolean lengthInvalid(int i) {
        return i >= length;
    }

    public void removeAll() {
        head = null;
        tail = null;
        length = 0;
    }

    public int getLength() {
        return length;
    }

    public void setLength(int length) {
        this.length = length;
    }

    public Node getNode() {
        return node;
    }

    public void setNode(Node node) {
        this.node = node;
    }
}
